/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ani.h"
#include <string>
#include <iostream>
#include <cstring>
#include <cassert>
#include <vector>
#include <algorithm>

[[maybe_unused]] static void GetStdString(ani_env *env, ani_string str, std::string &result)
{
    ani_size sz {};
    if (env->String_GetUTF8Size(str, &sz) != ANI_OK) {
        exit(1);
    }
    result.resize(sz + 1);

    if (env->String_GetUTF8SubString(str, 0, sz, result.data(), result.size(), &sz) != ANI_OK) {
        exit(1);
    }
    result.resize(sz);
}

[[maybe_unused]] static bool Equal(double a1, double a2)
{
    return std::abs(a1 - a2) < 0.000001F;
}

template <typename ValueType>
std::vector<ValueType> EtsArrToVec(ValueType *buf, ani_size sizeArr)
{
    if (buf == nullptr) {
        std::cerr << "Array empty, failed to get array";
        exit(1);
    }
    std::vector<ValueType> vec;
    vec.reserve(sizeArr);
    for (ani_size i = 0; i < sizeArr; i++) {
        vec.push_back(buf[i]);
    }
    return vec;
}

{% if type != "string" -%}
void print{{ Type }}Arr([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_array_{{ type }} arr)
{
    const uint32_t bufferSize = 5U;
    ani_size len, offset {0};
    env->Array_GetLength(arr, &len);
    ani_{{ type }} buff[bufferSize] = {};

    env->Array_GetRegion_{{ Type }}(arr, offset, len, buff);
    auto vec = EtsArrToVec<ani_{{ type }}>(buff, len);
    for (size_t i = 0; i < bufferSize; ++i) {
    {% if type == "double" or type == "float" -%}
        assert(Equal(buff[i], 1.1 * (i + 1)));
    {%- elif type == "boolean" -%}
        assert(buff[i] == (i + 1) % 2);
    {%- else %}
        assert(buff[i] == static_cast<int>(i + 1));
    {%- endif %}
    }
}
{%- else %}
void printStringArr([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_array_ref arr)
{
    ani_size sz;
    ani_ref ref;
    std::string str;
    env->Array_GetLength(arr, &sz);
    for (ani_size i = 0; i < sz; ++i) {
        env->Array_Get_Ref(arr, i, &ref);
        auto string = reinterpret_cast<ani_string>(ref);
        GetStdString(env, string, str);
        assert(str == std::to_string(i + 1));
    }
}
{%- endif %}

const std::vector<ani_native_function> g_{{ type }}Methods = {
    {"print{{ Type }}Arr", nullptr, reinterpret_cast<void *>(print{{ Type }}Arr)},
    {"printQuick{{ Type }}Arr", nullptr, reinterpret_cast<void *>(print{{ Type }}Arr)},
};

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }
    static const char *moduleName = "EtsAniTests";

    ani_module module;

    if (ANI_OK != env->FindModule(moduleName, &module)) {
        std::cerr << "Not found '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    }

    for (auto &m : g_{{ type }}Methods) {
        if (env->Module_BindNativeFunctions(module, &m, 1) != ANI_OK) {
            return ANI_ERROR;    
        }
    }
    *result = ANI_VERSION_1;
    return ANI_OK;
}
